//
// Created by lz on 3/4/21.
//

#include "net/Acceptor.h"

#include "base/Logging.h"
#include "net/EventLoop.h"
#include "net/InetAddress.h"
#include "net/SocketsOps.h"

#include <cerrno>
#include <fcntl.h>
#include <unistd.h>

using namespace lzweb;
using namespace lzweb::net;

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
:loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),  //createNonblockingOrDie返回一个接收TCP协议的socket
acceptChannel_(loop,acceptSocket_.fd()),
listening_(false),
idleFd_(::open("/dev/null",O_RDONLY|O_CLOEXEC))
{
	assert(idleFd_ >= 0);
	//一般将地址重用这个状态设为true
	acceptSocket_.setReuseAddr(true);
	acceptSocket_.setReusePort(reuseport);

	acceptSocket_.bindAddress(listenAddr);


	acceptChannel_.setReadCallback(
		std::bind(&Acceptor::handleRead, this)
	);
}

Acceptor::~Acceptor()
{
	acceptChannel_.disableAll();
	acceptChannel_.remove();
	::close(idleFd_);
}

void Acceptor::listen()
{
	loop_->assertInLoopThread();
	listening_ = true;
	acceptSocket_.listen();

	// enableReading 会将该fd的可读注册到poll中
	acceptChannel_.enableReading();
}

void Acceptor::handleRead()
{
	loop_->assertInLoopThread();
	InetAddress peerAddr;
	//FIXME loop until no more
	int connfd = acceptSocket_.accept(&peerAddr);

	if(connfd >= 0)
	{
		if(newConnectionCallback_)
		{
			newConnectionCallback_(connfd, peerAddr);
		}else
		{
			sockets::close(connfd);
		}

	}
	else
	{
		LOG_SYSERR << "in Acceptor::handleRead";
		// Read the section named "The special problem of
		// accept()ing when you can't" in libev's doc.
		// By Marc Lehmann, author of libev.
		if(errno == EMFILE) // EMFILE 一般在太多open files
		{
			::close(idleFd_);
			idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
			::close(idleFd_);
			idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
		}
	}

}
